/*
 * Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.spi.servicecontext;

import org.omg.CORBA.BAD_PARAM;
import org.omg.CORBA_2_3.portable.InputStream;
import com.sun.corba.se.spi.servicecontext.ServiceContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.impl.orbutil.ORBUtility;

/**
 * Internal class used to hold data about a service context class.
 */
public class ServiceContextData {

  private void dprint(String msg) {
    ORBUtility.dprint(this, msg);
  }

  private void throwBadParam(String msg, Throwable exc) {
    BAD_PARAM error = new BAD_PARAM(msg);
    if (exc != null) {
      error.initCause(exc);
    }
    throw error;
  }

  public ServiceContextData(Class cls) {
    if (ORB.ORBInitDebug) {
      dprint("ServiceContextData constructor called for class " + cls);
    }

    scClass = cls;

    try {
      if (ORB.ORBInitDebug) {
        dprint("Finding constructor for " + cls);
      }

      // Find the appropriate constructor in cls
      Class[] args = new Class[2];
      args[0] = InputStream.class;
      args[1] = GIOPVersion.class;
      try {
        scConstructor = cls.getConstructor(args);
      } catch (NoSuchMethodException nsme) {
        throwBadParam("Class does not have an InputStream constructor", nsme);
      }

      if (ORB.ORBInitDebug) {
        dprint("Finding SERVICE_CONTEXT_ID field in " + cls);
      }

      // get the ID from the public static final int SERVICE_CONTEXT_ID
      Field fld = null;
      try {
        fld = cls.getField("SERVICE_CONTEXT_ID");
      } catch (NoSuchFieldException nsfe) {
        throwBadParam("Class does not have a SERVICE_CONTEXT_ID member", nsfe);
      } catch (SecurityException se) {
        throwBadParam("Could not access SERVICE_CONTEXT_ID member", se);
      }

      if (ORB.ORBInitDebug) {
        dprint("Checking modifiers of SERVICE_CONTEXT_ID field in " + cls);
      }

      int mod = fld.getModifiers();
      if (!Modifier.isPublic(mod) || !Modifier.isStatic(mod) ||
          !Modifier.isFinal(mod)) {
        throwBadParam("SERVICE_CONTEXT_ID field is not public static final", null);
      }

      if (ORB.ORBInitDebug) {
        dprint("Getting value of SERVICE_CONTEXT_ID in " + cls);
      }

      try {
        scId = fld.getInt(null);
      } catch (IllegalArgumentException iae) {
        throwBadParam("SERVICE_CONTEXT_ID not convertible to int", iae);
      } catch (IllegalAccessException iae2) {
        throwBadParam("Could not access value of SERVICE_CONTEXT_ID", iae2);
      }
    } catch (BAD_PARAM nssc) {
      if (ORB.ORBInitDebug) {
        dprint("Exception in ServiceContextData constructor: " + nssc);
      }
      throw nssc;
    } catch (Throwable thr) {
      if (ORB.ORBInitDebug) {
        dprint("Unexpected Exception in ServiceContextData constructor: " +
            thr);
      }
    }

    if (ORB.ORBInitDebug) {
      dprint("ServiceContextData constructor completed");
    }
  }

  /**
   * Factory method used to create a ServiceContext object by
   * unmarshalling it from the InputStream.
   */
  public ServiceContext makeServiceContext(InputStream is, GIOPVersion gv) {
    Object[] args = new Object[2];
    args[0] = is;
    args[1] = gv;
    ServiceContext sc = null;

    try {
      sc = (ServiceContext) (scConstructor.newInstance(args));
    } catch (IllegalArgumentException iae) {
      throwBadParam("InputStream constructor argument error", iae);
    } catch (IllegalAccessException iae2) {
      throwBadParam("InputStream constructor argument error", iae2);
    } catch (InstantiationException ie) {
      throwBadParam("InputStream constructor called for abstract class", ie);
    } catch (InvocationTargetException ite) {
      throwBadParam("InputStream constructor threw exception " +
          ite.getTargetException(), ite);
    }

    return sc;
  }

  int getId() {
    return scId;
  }

  public String toString() {
    return "ServiceContextData[ scClass=" + scClass + " scConstructor=" +
        scConstructor + " scId=" + scId + " ]";
  }

  private Class scClass;
  private Constructor scConstructor;
  private int scId;
}
